import {
    ArcRotateCamera,
    Color3,
    Color4,
    CubeTexture,
    Effect,
    Engine,
    HemisphericLight,
    Mesh,
    MeshBuilder,
    Scene,
    ShaderMaterial,
    StandardMaterial,
    Texture,
    Vector3,
} from "@babylonjs/core";

//天空盒
export default class SkyBox {
    public engine: Engine;
    public scene: Scene;
    public canvas: HTMLCanvasElement;

    private updateAction: Map<number, (time: number) => {}> = new Map();
    private updateActionId: number = 0;

    public static skyBox: SkyBox;

    public constructor(canvas: HTMLCanvasElement) {
        this.canvas = canvas;
        this.engine = new Engine(canvas, true);
        this.scene = this.createScene();

        this.init();

        SkyBox.skyBox = this;
    }

    private init() {
        this.update(0);
    }

    private createScene() {
        const { engine, canvas } = this;
        let scene = new Scene(engine);
        scene.clearColor = new Color4(0, 0, 0, 1);

        const light = new HemisphericLight("defaultLight", new Vector3(1, 1, 0), scene);

        const camera = new ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 3, 10, new Vector3(0, 0, 0), scene);
        camera.attachControl(canvas, true);

        const box = MeshBuilder.CreateBox("box");


        //方法1
        {
            Effect.ShadersStore['Demo01VertexShader'] = `
                attribute vec3 position;
                attribute vec3 normal;
                attribute vec2 uv;
                uniform mat4 worldViewProjection;
                varying vec2 v_uv;
                void main(){
                    gl_Position = worldViewProjection * vec4(position, 1.0);
                    v_uv = uv;
                }
            `;
            Effect.ShadersStore['Demo01FragmentShader'] = `
                precision mediump float;
                varying vec2 v_uv;
                void main(){
                    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
                }
            `;

            const mat = new ShaderMaterial(
                'shader01',
                scene,
                {
                    vertex: 'Demo01',
                    fragment: 'Demo01',
                },
                {
                    attributes: ['position', 'normal', 'uv'],
                    uniforms: ['worldViewProjection']
                }
            );
            // const texture = new Texture('');
            // mat.setTexture('u_texture', texture);

            // box.material = mat;
        }

        //方法2
        {
            const mat = new ShaderMaterial(
                'shader01',
                scene,
                '../shader/shader01',
                {
                    attributes: ['position', 'normal', 'uv'],
                    uniforms: ['worldViewProjection']
                }
            );
            const texture = new Texture('./image/image01.png');
            mat.setTexture('u_Sampler', texture);

            box.material = mat;
        }

        return scene;
    }

    private update(time: number) {
        const { updateAction, scene } = this;
        updateAction.forEach((action, key) => {
            action(time);
        });
        scene.render();
        requestAnimationFrame((time) => {
            this.update(time);
        });
    }

    public setUpdateAction(action: (time: number) => {}, id: number = -1) {
        const { updateAction } = this;
        let curId = id;
        if (id == -1) {
            curId = this.updateActionId++;
        } else if (id >= this.updateActionId) {
            curId = this.updateActionId++;
        }
        updateAction.set(curId, action);
    }
}
